//------------------------------------------------------------------------------
// X2TK Graphic Toolkit
// Copyright (C) 2006-2008 Janusz Kowalski
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef X2TK_WIDGET_H
#define X2TK_WIDGET_H

#include "RawVector.h"

#include "SDL.h"
#include "fontSDL.h"
#include "x2tk_Rectangle.h"
#include "x2tk_Graphics.h"

namespace x2tk {

enum EV {
  EV_FOCUS
};
  
class Widget : public Rectangle {
  public:
    static Widget *NONE;
  private:
    Widget *parent_;
    bool visible;
    // Level of the widget. No widget can be placed above widget of higher
    // level. No widget can be placed below widget of lower level.
    // Default value is 0. Can be negative.
    int level_;
    // List of children.
    RawVector<Widget*> children;

    // Simply move child to the top or to the bottom. Level is taken into
    // account. Should not throw exceptions.
    void move_(Widget *child, bool top);
    
    // Local focus owner.
    Widget *focusOwner_;

  public:
    // Flag used for focus traversal.
    // !! This should be function-modifyable bit set.
    bool fEvFocus_;
  public:
    virtual void event(EV ev);
    
    // Focus-related functions.
    
    // Sets local focus owner. It must be a child of this widget or 0.
    void setFocusOwner(Widget *child);
    
    // Gets local focus owner.
    Widget *getFocusOwner();
    
    // Global focus state.
    virtual bool hasFocus();
    
    // Give global focus to this widget.
    void setFocus();

    // Constructor
    Widget(int x, int y, int w, int h);
    
    // Destructor
    ~Widget();
    
    /// Moves this widget to the top of its current container.
    void toTheTop();
    
    /// The same, but moves it to the bottom.
    void toTheBottom();
    
    /// Adds child, if it's not already one of the children of this widget
    /// (otherwise nothing happens). The child is removed from its previous
    /// parent, if any. Child is always added at the top.
    /// If this function fails (unlikely), the child becomes parentless.
    /// @param child child to add; cannot be NULL nor ancestor of this
    void add(Widget *child);
    
    /// Removes child from the widget, and sets its parent to 0.
    /// @param child child to remove; cannot be NULL
    void remove(Widget *child);
    
    // Number of children.
    inline int nChildren() { return children.size(); }
    
    inline Widget*& operator[] (int i) { return children[i]; }
    
    // Returns index of given child in the children list or invalid index if not found.
    inline unsigned indexOf__(Widget *wdg) { return children.indexOf__(wdg); }
  
    // Access to the 'level' field of this widget.
    inline int getLevel() { return level_; }
    inline void setLevel(int level) { level_ = level; }
    // Self-explanatory.
    Widget *getParent();
    // Draw this widget in the given context.
    virtual void draw(GraphicContext &g) { };
    // Ruch myszki nad widgetem. Container nie powinien przekazywać ruchów
    // myszki widgetowi, nad którym myszka się nie znajduje, chyba że jest on
    // w stanie śledzenia myszki. Może to być przydatne np. w przypadku pasków
    // przewijania, gdy naciskamy myszką na pasku, a nastepnie nie musumy już
    // jej trzymać dokładne nad nim.
    virtual void mouseMove(int x, int y) { };
    
    // Delikatne dotknięcie wskaźnikiem (dotykamy tam, gdzie przemieściliśmy wskaźnik)
    // Jeżeli widget "chciał" wskaźnik, to zwraca prawdę
    virtual bool mouseDown(int type);
    virtual void mouseUp(int type) { };
    // Można różnie dotknąć widgetu. Delikatnie i mocno. Lewym i prawym palcem.
    // Można przekręcić, przesunąć, itp.

    // Get local mouse position.
    virtual short getMouseX();
    virtual short getMouseY();
    
    // Function called in the case of action (like button press, mouse click,
    // etc.). By default it passes action to the parent widget.
    virtual void action(int id, Widget *origin);
    
    // Visibility.
    void setVisible(bool v);
    inline bool isVisible() {return visible;}
    
    // Called by the parent container if it changes its size or by Widget's
    // setParent method.
    virtual void layout() { }
    
    virtual void event_key(const SDL_KeyboardEvent& key);
    
    virtual void timed(int id) { };
    
    // Position of the child is always relative to the position of the top-left
    // corner of its parent. Following functions return position of the widget
    // relative to its parent and any of its ancestors.
    
    inline int getX() {
      return Rectangle::getX();
    }
    
    inline int getY() {
      return Rectangle::getY();
    }
    
    // w.getX(w) is 0
    virtual int getX(const Widget *ancestor) {
      Widget *p = this; int x=0;
      while(p!=0) {
        if(p==ancestor) return x;
        x+=p->getX(); p=p->getParent();
      }
      throw "Error: Wrong ancestor.";
    }
    
    // w.getY(w) is 0
    virtual int getY(const Widget *ancestor) {
      Widget *p = this; int y=0;
      while(p!=0) {
        if(p==ancestor) return y;
        y+=p->getY(); p=p->getParent();
      }
      throw "Error: Wrong ancestor.";
    }
    
    // w.isDescendantOf(w) is true.
    virtual bool isDescendantOf(const Widget *w) {
      for(Widget *p=this; p!=0; p=p->getParent()) {
        if(p==w) return true;
      }
      return false;
    }
};

} // namespace x2d

#endif
